package com.demo.druid.component.datasource.aspect;

import com.demo.druid.component.datasource.DynamicDataSourceContextHolder;
import com.demo.druid.component.datasource.annotation.DataSource;
import com.demo.druid.component.datasource.annotation.DataSourceKey;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;

/**
 * @author wangjianhua
 * @Description 切面建议使用一种即可 不过注解形式会优于包切面
 * @date 2021/9/1/001 15:06
 **/
@Component
@Aspect
public class DataSourceAspect {

    private static final Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);

    /**
     * 查询的关键字 一般都要包含这些 一定要规范
     */
    private final String[] QUERY_PREFIX = {"get","find","query"};

    /**
     * service方法切面 ===>读写分离
     */
    @Pointcut("execution(public * com.demo.druid.modules.*.*.service.impl.*Impl.*(..))")
    public void pointCutService(){

    }

    @Pointcut("@annotation(com.demo.druid.component.datasource.annotation.DataSource)")
    public void pointCut(){
    }

    @Before("pointCut()")
    public void before(JoinPoint point) throws Throwable{
        logger.info("进入注解面");
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DataSource dataSource = method.getAnnotation(DataSource.class);
        if(!StringUtils.isEmpty( dataSource.dataSourceKey().toString())){
            logger.info("设置为{}数据源",dataSource.dataSourceKey().toString());
            DynamicDataSourceContextHolder.setDataSourceKey(dataSource.dataSourceKey().toString());
            logger.debug("选择数据源到 [{}] ]",
                    DynamicDataSourceContextHolder.getDataSourceKey());
        }


    }

    @After("pointCut()")
    public void after(JoinPoint point){
        DynamicDataSourceContextHolder.clearDataSourceKey();
        logger.debug("重置数据源 [{}] 在方法 [{}]中",DynamicDataSourceContextHolder.getDataSourceKey(),point.getSignature());

    }

    /**
     * 通过反射获取方法名 然后知道该方法为查询方法 aop代理到该方法中进行数据源切换
     */
    @Around("pointCutService()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        boolean isQueryMethod = isQueryMethod(joinPoint.getSignature().getName());
        if(isQueryMethod){
            DynamicDataSourceContextHolder.useSlaveDataSource();
            logger.info("切换数据源到[{}]在方法[{}]中",
                    DynamicDataSourceContextHolder.getDataSourceKey(),joinPoint.getSignature());
            logger.info("当前使用数据源为:{}",DynamicDataSourceContextHolder.getDataSourceKey());
        }
//        DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.master.name());
        try
        {
            return joinPoint.proceed();
        }
        finally {
            DynamicDataSourceContextHolder.clearDataSourceKey();
        }

    }

    /**
     * 判断主从分离时的方法是否为查询方法
     * @param methodName 方法名
     * @return boolean
     */
    private Boolean isQueryMethod(String methodName){
        for (String queryPrefix : QUERY_PREFIX) {
            if(methodName.contains(queryPrefix)){
                return true;
            }
        }
        return false;
    }
}
